import csv
from typing import List, Tuple
from prettytable import PrettyTable
import copy
import math
import numpy as np
import json

class Seg:
    """
    时间段，可以以优先级进行排序
    """
    def __init__(self, upperBound: int, weight: int, day: int, cls: int = -1):
        self.cls, self.day = cls, day
        self.upperBound = upperBound  # 上限人数
        self.priority = -weight  # 该时间段有空的人数
        self.members = []

    def __str__(self) -> str:
        return str((self.cls, self.day, self.count, self.upperBound, self.priority))

    @property
    def count(self):
        return len(self.members)

    def addMember(self, info):
        self.members.append(info)

    def __lt__(self, other):
        """
        小于比较，排在前面的优先级较低
        """
        b1 = self.count < self.upperBound
        b2 = other.count < other.upperBound #看看二者到没到上限

        if b1 and b2:
            return self.priority < other.priority  # 都没到上限，按优先级
        elif b1 and not b2:
            return False
        elif not b1 and b2:
            return True  # 谁没到上限谁优先
        else:
            return self.count > other.count  # 都到上限了取人少的


class Member:
    """
    成员，分别用于部员和部长
    """
    def __init__(self, data: dict):
        self.id = data.get("id")
        self.name: str = data.get("name")
        self.identity = data.get("identity")
        self.times = data.get("time")

    def __str__(self) -> str:
        return self.name

    def arrange(self, timeList: List[List[Seg]]):
        """
        将成员安排在某个时间段。
        简而言之就是把这个人所有有空的时间段按优先级排序，选取最高优先级的那个进行安置。
        """
        segs: List[Seg] = []
        for time in self.times:
            segs.append(timeList[time[0]][time[1]])

        segs.sort(reverse=True)

        segs[0].addMember(self.name)

    def getTimes(self):
        """
        以numpy.array的矩阵形式返回有空的时间，有空的地方数值为1。
        这是为了方便统计有多少人在某个时间段有空。
        之所以numpy是因为同形状array可以直接对应元素相加。
        """
        array = np.zeros([6, 5], int)
        for time in self.times:
            cls, day = time
            array[cls][day] = 1
        return array


class Algorithm:
    def __init__(self, data: List):
        self.data = data
        self.members, self.weight = self.processMembers()
        self.upperBound = 1

    def processMembers(self):
        """
        创建所有成员，顺带着把权重统计了
        """
        members = []
        weight = np.zeros([6, 5], int)
        for data in self.data:
            m = Member(data)
            members.append(m)
            weight += m.getTimes()

        return members, weight

    def calculate(self):
        timeTable = [
            [Seg(self.upperBound, self.weight[cls][day], day, cls) for day in range(5)]
            for cls in range(6)]

        result = [[[] for _ in range(5)] for _ in range(6)]

        for mem in self.members:
            mem.arrange(timeTable)

        for i, row in enumerate(timeTable):
            for j, col in enumerate(row):
                result[i][j] = col.members

        return result


class Duty(object):
    def __init__(self):
        self.csv_path = 'data.csv'
        self.time_info_json = []

    def parse_csv(self):
        """将csv解析成json"""
        f_csv = csv.reader(open(self.csv_path, 'r', encoding='utf-8'))
        header = next(f_csv)
        for id, line in enumerate(f_csv):
            name = line[1]
            identity = 1 if line[2] == '部员' else 0
            time = []
            time_info = [i.split('：') for i in line[3].split('），')]
            try:
                time_info[-1][1] = time_info[-1][1][:-1]
                for info in time_info:
                    row = int(info[0]) - 1
                    col_index = [int(i) - 1 for i in info[1][1:].split('，')]
                    for col in col_index:
                        time.append([row, col])
                self.time_info_json.append({
                    'id': id,
                    'name': name,
                    'identity': identity,
                    'time': time
                })
            except:
                print(f"数据异常：{name}")

    def duty_sort(self):
        """排课表核心算法"""
        if not self.time_info_json:
            return None
        else:
            members = []
            ministers = []
            for data in self.time_info_json:
                if data.get("identity") == 1:
                    members.append(data)
                else:
                    ministers.append(data)

            A = Algorithm(members).calculate()
            B = Algorithm(ministers).calculate()
            return A, B

    @staticmethod
    def print_table(table_info):
        """输出课表"""
        table = PrettyTable(['', '周一', '周二', '周三', '周四', '周五', ])
        for index, info in enumerate(table_info):
            info = ['、'.join(i) for i in info]
            table.add_row([f'第{index + 1}节课'] + info)
        print(table)

    def run(self):
        self.parse_csv()
        members, ministers = self.duty_sort()
        if members is None or ministers is None:
            print('排序失败')
        else:
            self.print_table(members)
            self.print_table(ministers)


if __name__ == '__main__':
    d = Duty()
    d.run()
